import * as THREE from "three";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import { Model } from "./model";
import { ClearCommand } from "./render-command-system/command/ClearCommand";
import { CommandManager } from "./render-command-system/command/CommandManager";
import { ModelBoundingCommand } from "./render-command-system/command/ModelBoundingCommand";
import { ModelCommand } from "./render-command-system/command/ModelCommand";
import { RenderCommandSystem } from "./render-command-system/RenderCommandSystem";

export class CxImpl {

    renderer: THREE.WebGLRenderer;
    control!: OrbitControls;
    model: Model;
    camera: THREE.Camera;
    rcs: RenderCommandSystem;
    private animationBind = this.animation.bind(this);
    private renderCbBind = this.renderCb.bind(this);
    private stats!: Stats;
    private targetFps = 60;
    private oldCameraMatrixElements: number[] = [];
    private cameraChanged = false;
    private firstFrame = true;
    private cmdManager: CommandManager;
    constructor() {
        console.log(1821);

        this.rcs = new RenderCommandSystem();
        const frameBudget = 1000 / this.targetFps;
        this.rcs.setFrame(frameBudget);

        this.stats = Stats();
        this.model = new Model();

        this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10000);
        this.camera.position.z = 1000;
        this.oldCameraMatrixElements = this.camera.matrixWorld.elements;

        this.renderer = new THREE.WebGLRenderer({
            antialias: true,
            preserveDrawingBuffer: true,
        });
        this.renderer.autoClear = false;
        this.renderer.sortObjects = false;
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setAnimationLoop(this.animationBind);

        this.control = new OrbitControls(this.camera, this.renderer.domElement);

        document.body.appendChild(this.renderer.domElement);
        document.body.appendChild(this.stats.dom);

        this.cmdManager = CommandManager.getIns();
        this.cmdManager.registerCmd(ClearCommand.name, new ClearCommand(this.rcs));
        this.cmdManager.registerCmd(ModelCommand.name, new ModelCommand(this.rcs));
        this.cmdManager.registerCmd(ModelBoundingCommand.name, new ModelBoundingCommand(this.rcs));
    }
    private animation(time: number) {
        this.tick(time);
        this.control.update();
        this.stats.update();
    }
    /**
     *
     * @param scene
     */
    private renderCb(scene?: THREE.Scene): void {
        if (scene) {
            this.renderer.render(scene, this.camera);
        } else {
            this.renderer.clear();
        }
    }
    /**
     *
     * @param highResTimeStamp
     * tick三件事：决定是否有东西动了。如果需要渲染，则安排一个有次序的渲染命令列表。执行当前渲染列表。
     */
    private tick(highResTimeStamp: number) {

        if (this.cameraChanged || this.firstFrame) {
            this.firstFrame = false;
            this.rcs.beginCommandSet();
            this.rcs.addCMD(this.cmdManager.getCmd(ClearCommand.name));
            this.rcs.addParam("renderer", this.renderer);
            this.rcs.addCMD(this.cmdManager.getCmd(ModelCommand.name));
            this.rcs.addParam("renderCb", this.renderCbBind);
            this.rcs.addParam("model", this.model);
            this.rcs.addCMD(this.cmdManager.getCmd(ModelBoundingCommand.name));
            this.rcs.addParam("renderCb", this.renderCbBind);
            this.rcs.addParam("model", this.model);
            this.rcs.endCommandSet();
        }
        this.rcs.executeCommandList();

        this.cameraChanged = !this.oldCameraMatrixElements.every((v, i) => {
            return v === this.camera.matrixWorld.elements[i];
        });

        this.oldCameraMatrixElements = this.camera.matrixWorld.elements.map(v => { return v; });
    }
}
const cx = new CxImpl();
(window as any).cx = cx;
